package cn.highedu.coolshark.account.security;

import cn.highedu.coolshark.account.pojo.vo.AdminLoginInfoVO;
import cn.highedu.coolshark.common.ServiceCode;
import cn.highedu.coolshark.common.ServiceException;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.ConcurrentSkipListSet;

@Slf4j
@Service
public class JwtTokenHandler {

    @Value("${jwt.secret-key}")
    private String secretKey;

    @Value("${jwt.duration-in-minute}")
    private long durationInMinute;

    // 登出黑名单，存储已经登出的用户的token，用于校验token是否有效
    private static final ConcurrentSkipListSet<String> logoutBlacklist = new ConcurrentSkipListSet<>();

    //定时清理登出黑名单中的过期失效的token,每小时执行一次
    @Scheduled(fixedRate = 60 * 60 * 1000)
    public void clearLogoutBlacklist() {
        log.debug("开始清理登出黑名单");
        Date now = new Date();
        logoutBlacklist.removeIf(token -> {
            try {
                Claims claims = Jwts.parser()
                        .setSigningKey(secretKey)
                        .parseClaimsJws(token)
                        .getBody();
                Date exp = claims.getExpiration();
                return now.after(exp);
            } catch (Exception e) {
                log.error("解析Token失败", e);
                return true;
            }
        });
        log.debug("清理登出黑名单成功");
    }

    public JwtTokenHandler() {
        log.debug("JwtTokenHandler对象创建");
    }

    /**
     * 将JWT字符串加入登出黑名单
     */
    public void addLogoutBlacklist(String jwt) {
        logoutBlacklist.add(jwt);
    }

    /**
     * 判断JWT字符串是否在登出黑名单中
     */
    public boolean isLogoutToken(String jwt) {
        return logoutBlacklist.contains(jwt);
    }


    /**
     * 将UserDetailsImpl对象转换为JWT字符串
     */
    public String createToken(UserDetailsImpl userDetails){
        AdminLoginInfoVO adminLoginInfoVO = userDetails.getAdmin();
        return createToken(adminLoginInfoVO);
    }

    /**
     * 将JWT字符串解析为UserDetailsImpl对象
     */
    public UserDetailsImpl parseJwtToken(String jwt){
        AdminLoginInfoVO adminLoginInfoVO = parseToken(jwt);
        return new UserDetailsImpl(adminLoginInfoVO);
    }

    /**
     * 将 AdminLoginInfoVO 对象转换为 JWT 字符串
     * @param adminLoginInfoVO AdminLoginInfoVO 对象
     *                         该对象封装了管理员的登录信息
     */
    public String createToken(AdminLoginInfoVO adminLoginInfoVO) {
        log.debug("开始创建JWT，参数：{}", adminLoginInfoVO);
        // 从 AdminLoginInfoVO 对象中获取数据
        String subject = adminLoginInfoVO.getUsername();
        Date now = new Date();
        Date exp = new Date(now.getTime() + durationInMinute * 60 * 1000);
        Long id = adminLoginInfoVO.getId();
        Integer enabled = adminLoginInfoVO.getEnable();
        String roles = String.join(",", adminLoginInfoVO.getRoles());
        String permissions = String.join(",", adminLoginInfoVO.getPermissions());
        // 创建 JWT 字符串
        String jwt = Jwts.builder()
                // 设置 JWT 的标准信息
                .setSubject(subject)
                .setIssuedAt(now)
                .setExpiration(exp)
                // 设置 JWT 的载荷
                .claim("id", id)
                .claim("enabled", enabled)
                .claim("roles", roles)
                .claim("permissions", permissions)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        log.debug("创建JWT成功，结果：{}", jwt);
        return jwt;
    }
    /**
     * 将 JWT 字符串解析为 AdminLoginInfoVO 对象
     * @param jwt JWT 字符串
     *            该字符串封装了管理员的登录信息
     *            该字符串由 createToken 方法创建
     *            该字符串由客户端提交
     *            该字符串由服务端解析
     * @throws ServiceException JWT 字符串无效，则抛出异常
     * @return AdminLoginInfoVO 对象
     */
    public AdminLoginInfoVO parseToken(String jwt) {
        log.debug("开始解析JWT，参数：{}", jwt);
        try {
            // 解析 JWT 字符串
            Claims claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();
            // 从 JWT 字符串中获取数据
            Long id = Long.valueOf(claims.get("id").toString());
            String username = claims.get("sub").toString();
            Integer enabled = Integer.valueOf(claims.get("enabled").toString());
            String roles = claims.get("roles").toString();
            String permissions = claims.get("permissions").toString();
            // 创建 AdminLoginInfoVO 对象
            AdminLoginInfoVO adminLoginInfoVO = new AdminLoginInfoVO();
            adminLoginInfoVO.setId(id);
            adminLoginInfoVO.setUsername(username);
            adminLoginInfoVO.setEnable(enabled);
            adminLoginInfoVO.setRoles(Arrays.asList(roles.split(",")));
            adminLoginInfoVO.setPermissions(Arrays.asList(permissions.split(",")));
            log.debug("解析JWT成功，结果：{}", adminLoginInfoVO);
            return adminLoginInfoVO;
        } catch (ExpiredJwtException e) {
            log.error("JWT已过期", e);
            throw new ServiceException(ServiceCode.ERR_JWT_EXPIRED, "JWT已过期");
        } catch (SignatureException e) {
            log.error("解析JWT失败", e);
            throw new ServiceException(ServiceCode.ERR_JWT_SIGNATURE, "令牌无效");
        } catch (Exception e) {
            log.error("解析Token失败", e);
            log.error("解析JWT失败", e);
            throw new ServiceException(ServiceCode.ERR_JWT_MALFORMED, "令牌无效");
        }
    }


}
